package com.yxt.auth.config;
import com.yxt.admin.dto.UserDto;
import com.yxt.auth.entity.UserJwt;
import com.yxt.auth.feign.AdminFeignApi;
import com.yxt.common.constant.Constant;
import com.yxt.common.utils.ServletUtils;
import com.yxt.common.utils.ip.AddressUtils;
import com.yxt.common.utils.ip.IpUtils;
import com.yxt.common.utils.redis.RedisCache;
import eu.bitwalker.useragentutils.UserAgent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.concurrent.TimeUnit;

/*****
 * 自定义授权认证类
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {


//    @Autowired
//    private UserFeignApi userFeign;

    @Autowired
    private AdminFeignApi adminFeignApi;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    // 令牌有效期（默认30分钟）
    @Value("${token.expireTime}")
    private int expireTime;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private RedisCache redisCache;

    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;

    /****
     * 自定义授权认证
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
      //取出身份，如果身份为空说明没有认证
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if(authentication==null){
            throw new UsernameNotFoundException("用户名或密码错误!");
        }

        if (StringUtils.isEmpty(username)) {
            throw new UsernameNotFoundException("用户名或密码错误!");
        }

        //根据用户名查询用户信息
        UserDto userDto = adminFeignApi.findByUserName(username);


        /**
         * 将userDot信息存入redis中，accessToken中不应该存入太多的信息，只存放必要的信息。
         * 如果accessToken中存入太多的信息会有以下3个问题
         * 1 accessToken串太长
         * 2 如果存放额外的信息，还需要借助于CustomTokenEnhancer这个类，这个类里面
         * 还需要查询出userDto然后存入，二次查询性能低下
         * 3 jwt无法做到立即失效，用户立即退出。但是借助于redis就可以。
         *
         */
          //todo 后期修改为 clientId uername userDto模式 加上了租户这个概念
        saveUserDtoToRedis(String.valueOf(userDto.getUser().getUserId()),userDto);

//        //创建User对象
        String permissions = "/user/hello," +
                "/user/list," +
                "/user/**," +
                "/formitem/**," +
                "/generator/**," +
                "/admin/**," +
                "/admin/getInfo," +
                "/area/**," +
                "/admin/getRouters";
//        UserJwt userDetails = new UserJwt(username,resp.getData().getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList(permissions));
//String redisCode = stringRedisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);

        List<GrantedAuthority> grantedAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList(permissions);
        UserJwt userDetails =new UserJwt(username,userDto.getPassword(),grantedAuthorities);
        userDetails.setComny("阿里巴巴");
        userDetails.setId(userDto.getUser().getUserId());

        return userDetails;
//        return User.withUsername(username)
//                .password(passwordEncoder.encode("123456"))
//                .authorities("ROLE_ADMIN")
//                .build();
    }

    /**
     *
     * @param key
     * @param userDto
     * todo accessToken失效时间应该和redis失效时间一致。
     * 可以将失效时间配置到配置文件中，然后新增到oauth_client_details表中
     * 程序中可以直接获取配置文件中配置的失效时间
     */
    private void saveUserDtoToRedis(String key,UserDto userDto){
        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        userDto.setIpaddr(ip);
        userDto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
        userDto.setBrowser(userAgent.getBrowser().getName());
        userDto.setOs(userAgent.getOperatingSystem().getName());
        userDto.setLoginTime(System.currentTimeMillis());
        userDto.setExpireTime(userDto.getLoginTime() + expireTime * MILLIS_MINUTE);
        // 根据uuid将loginUser缓存
       // String userKey = getTokenKey(loginUser.getToken());
        redisCache.setCacheObject(Constant.USER_ACCOUNT_KEY+key, userDto, expireTime, TimeUnit.MINUTES);
    }
}
